from datetime import datetime
from decimal import Decimal

from django.db import transaction
from django_redis import get_redis_connection
from rest_framework import serializers
from goods.models import SKU
from orders.models import OrderInfo, OrderGoods


class OrderSKUSerializer(serializers.ModelSerializer):
    """订单结算商品的序列化器类"""
    count = serializers.IntegerField(label='结算的商品数量')

    class Meta:
        model = SKU
        fields = ('id', 'name', 'price', 'default_image_url', 'count')


class OrderSerializer(serializers.ModelSerializer):
    """订单创建的序列化器类"""

    class Meta:
        model = OrderInfo
        fields = ('order_id', 'address', 'pay_method')
        read_only_fields = ('order_id',)
        extra_kwargs = {
            'address': {
                'write_only': True,
                'required': True,
            },
            'pay_method': {
                'write_only': True,
                'required': True,
            }
        }

    def create(self, validated_data):
        """创建并保存订单的数据"""
        # 获取adderss和pay_method
        address = validated_data['address']
        pay_method = validated_data['pay_method']

        # 组织订单参数
        # 获取user
        user = self.context['request'].user

        # 订单id: 年月日时分秒+下单用户id
        order_id = datetime.now().strftime('%Y%m%d%H%M%S') + '%010d' % user.id

        # 订单商品的总数量和实付款
        total_count = 0
        total_amount = 0

        # 运费: 10
        freight = Decimal(10)

        # 订单状态
        if pay_method == 1:  # 货到付款
            status = 2  # 待发货

        else:  # 在线支付
            status = 1  # 待支付

        # 建立redis数据库链接
        redis_conn = get_redis_connection('cart')
        # 从redis购物车中获取用户所要购买的商品的id(redis set购物车中勾选的商品id)
        cart_selected_key = 'cart_select_%s' % user.id

        sku_ids = redis_conn.smembers(cart_selected_key)

        # 从redis hash中获取用户购物车中添加的商品id和对应数量count
        cart_key = 'cart_%s' % user.id

        cart_dict = redis_conn.hgetall(cart_key)

        with transaction.atomic():
            # with 语句块下面的代码,凡是涉及到数据库操作的代码,在进行数据库操作时,都会放在同一个事务中

            # 设置事务的保存点
            sid = transaction.savepoint()

            try:
                # 1) 向订单基本信息表中添加一条记录
                order = OrderInfo.objects.create(
                    order_id=order_id,
                    user=user,
                    address=address,
                    total_count=total_count,
                    total_amount=total_amount,
                    freight=freight,
                    pay_method=pay_method,
                    status=status
                )
                # 2) 订单包含几个商品,就向订单商品表中添加几条记录

                for sku_id in sku_ids:
                    # 获取用户所要购买的该商品的数量
                    count = cart_dict[sku_id]
                    count = int(count)

                    # 根据sku_id获取商品的信息
                    """
                    悲观锁解决并发问题
                    # select * from tb_sku where id=<sku_id>;
                    # sku = SKU.objects.get(id=sku_id) 
                    
                    # select * from tb_sku where id=<sku_id> for update;
                    sku = SKU.objects.select_for_update().get(id=sku_id)
                    """

                    for i in range(3):
                        # 乐观锁解决并发问题
                        # 根据sku_id获取商品的信息
                        # select * from tb_sku where id=<sku_id>
                        sku = SKU.objects.get(id=sku_id)
                        # 判断商品的库存
                        if count > sku.stock:
                            # 回滚事务到sid保存点
                            transaction.savepoint_rollback(sid)
                            raise serializers.ValidationError('商品的库存不足')

                        # 记录商品的原始库存
                        origin_stock = sku.stock
                        new_stock = origin_stock - count
                        new_sales = sku.sales + count

                        # 减少对应商品的库存,增加销量
                        # update tb_sku set stock=<new_stock>, sales=<new_sales>  where id=<sku_id> and stock=<origin_stock>;

                        # 返回的结果是行数,判断res的值为不为0
                        res = SKU.objects.filter(id=sku_id, stock=origin_stock).update(stock=new_stock, sales=new_sales)

                        if res == 0:
                            # 跟新失败,重新进行尝试
                            if i == 2:
                                # 尝试了3次,更新仍然失败,下单失败
                                transaction.savepoint_rollback(sid)
                                raise serializers.ValidationError('下单失败2')
                            continue

                        # sku.stock -= count
                        # sku.sales += count
                        # sku.save()

                        # 向订单商品表中添加一条记录
                        OrderGoods.objects.create(
                            order=order,
                            sku=sku,
                            count=count,
                            price=sku.price
                        )

                        # 累加计算订单商品的总数量和总金额
                        total_count += count
                        total_amount += sku.price * count
                        # 跟新成功 跳出循环
                        break

                # 实际付款
                total_amount += freight
                order.total_amount = total_amount
                order.total_count = total_count
                order.save()
            except serializers.ValidationError:
                # 继续抛出错误
                raise
            except Exception:
                # 回滚事务到保存点
                transaction.savepoint_rollback(sid)
                raise serializers.ValidationError('下单失败1')

        # 3) 清除redis中对应的购物车
        pl = redis_conn.pipeline()
        pl.hdel(cart_key, *sku_ids)
        pl.srem(cart_selected_key, *sku_ids)
        pl.execute()

        return order
